﻿// <copyright project="Salient.SqlServer.NHibernate" file="HierarchyIdUserType.cs" company="Sky Sanders">
// This source is a Public Domain Dedication.
// Please see http://spikes.codeplex.com/ for details.   
// Attribution is appreciated
// </copyright> 
// <version>1.0</version>
using System;
using System.Data;
using System.IO;
using NHibernate;
using NHibernate.SqlTypes;
using NHibernate.UserTypes;
using Salient.SqlServer.Types;

namespace Salient.SqlServer.NHibernate
{
    /// <summary>
    /// This class is a NO-GO.
    /// 
    /// /// TODO: try implementing the Usertype by getting a string from the reader
    /// 
    /// SqlHierarchyId is only Binary Serializable. NH chokes on it
    /// Could implement a wrapper
    /// </summary>
    public class HierarchyIdUserType : IUserType
    {
        #region IUserType Members

        public object NullSafeGet(IDataReader rs, string[] names, object owner)
        {
            if (names.Length != 1)
                throw new InvalidOperationException("names array has more than one element. can't handle this!");

            object obj = NHibernateUtil.BinaryBlob.NullSafeGet(rs, names[0]);
            if (obj == null) return null;
            var bytes = obj as byte[];

            if (bytes == null
                /*i know bytes are not null, but i don't like ignoring blue squigglies */
                || bytes.Length == 0)
            {
                return HierarchyId.Null;
            }

            var returnValue = new HierarchyId();

            using (var input = new MemoryStream(bytes))
            {
                using (var r = new BinaryReader(input))
                {
                    returnValue.Read(r);
                }
            }

            return returnValue;
        }


        public void NullSafeSet(IDbCommand cmd, object value, int index)
        {
            var parameter = (IDataParameter) cmd.Parameters[index];
            if (value == null)
                parameter.Value = DBNull.Value;
            else
            {
                var obj = (HierarchyId) value;
                using (var output = new MemoryStream())
                {
                    using (var w = new BinaryWriter(output))
                    {
                        obj.Write(w);
                    }
                    parameter.Value = output.ToArray();
                }
            }
        }

        /// <summary>
        /// This might be where it is choking. SqlHierarchyId is struct - cannot be null
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public new bool Equals(object x, object y)
        {
            var xdocX = (HierarchyId) x;
            var xdocY = (HierarchyId) y;
            return xdocY.ToString() == xdocX.ToString();
        }

        public int GetHashCode(object x)
        {
            return x.GetHashCode();
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public object DeepCopy(object value)
        {
            var toCopy = (HierarchyId) value;

            var copy = new HierarchyId(toCopy.Inner);

            return copy;
        }

        public object Replace(object original, object target, object owner)
        {
            throw new NotImplementedException();
        }

        public object Assemble(object cached, object owner)
        {
            return DeepCopy(cached);
        }

        public object Disassemble(object value)
        {
            return DeepCopy(value);
        }

        /// <summary>
        /// TODO: Try getting a string here.....
        /// </summary>
        public SqlType[] SqlTypes
        {
            get { return new[] {NHibernateUtil.BinaryBlob.SqlType}; }
        }

        public Type ReturnedType
        {
            get { return typeof (HierarchyId); }
        }

        public bool IsMutable
        {
            get { return true; }
        }

        #endregion
    }
}